/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.cloudfarming.client.model;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import nl.cloudfarming.client.db.EntityManagerProvider;
import nl.cloudfarming.client.logging.AppLogFactory;
import nl.cloudfarming.client.logging.AppLogger;

/**
 * base model service containing utility methods to simplify service implementations
 *
 * @author Timon Veenstra
 */
public abstract class CommonModelService extends DataProvider implements BeanService {

    private static final AppLogger LOG = AppLogFactory.getLogger(CommonModelService.class);

    /**
     * starts a transaction if no transaction is active yet
     */
    protected void startTransaction() {
        assert getEntityManager() != null;
        assert getEntityManager().getTransaction() != null;

        if (!getEntityManager().getTransaction().isActive()) {
            getEntityManager().getTransaction().begin();
        }
    }

    @Override
    public void save(Bean bean) {
        startTransaction();
        merge((Persistable) bean);
        commit();
    }

    /**
     * getModuleName should be implemented by subclasses to determine module for persistence unit
     * @return
     */
    protected abstract String getModuleName();

    protected EntityManager getEntityManager() {
        return EntityManagerProvider.getDefault().getEntityManager(getModuleName());
    }

    /**
     * commit the active transaction
     * a transaction should be active
     *
     * @see EntityTransaction#commit()
     */
    protected void commit() {
        assert getEntityManager().getTransaction().isActive();
        getEntityManager().getTransaction().commit();
    }

    /**
     * flush the entity manager
     *
     * @see EntityManager#flush()
     */
    protected void flush() {
        getEntityManager().flush();
    }

    /**
     * Remove persistable entity
     * @param persistable Persistable to be removed
     */
    public void remove(Persistable persistable) {
        getEntityManager().remove(persistable);
    }

    /**
     * Persists a persistable object in a transaction
     * should not be used combined with other transactional methods
     *
     * @param persistable should be new to the database
     * @see EntityManager#persist(java.lang.Object)
     */
    protected void persistInTransaction(Persistable persistable) {
        try {
            startTransaction();
            getEntityManager().persist(persistable);
            getEntityManager().getTransaction().commit();
        } catch (Exception ex) {
            LOG.error("exception while trying to persist in transaction", ex);
            
        }
    }

    /**
     * Merges and persists a persistable object in a transaction
     * should not be used combined with other transactional methods
     *
     * @param persistable should exist in the database
     * @see EntityManager#persist(java.lang.Object)
     * @see EntityManager#merge(this)
     */
    protected void mergeAndPersistInTransaction(Persistable persistable) {
        startTransaction();
        merge(persistable);
        getEntityManager().getTransaction().commit();
    }

    /**
     * @param persistable
     * @see EntityManager#merge(this)
     */
    protected void merge(Persistable persistable) {
        if (persistable.getId() == 0) {
            getEntityManager().persist(persistable);
        } else {
            persistable = getEntityManager().merge(persistable);
        }
    }

    /**
     * Find all Fields
     * 
     * Original TODO said: "Move to Field" but this method is used outside FieldArea module (used by LinkSensorTask)
     * @return a List of Fields
     */
    public List<Field> findAllFields() {
        startTransaction();
        Query query = getEntityManager().createNamedQuery(Field.Q_FIND_ALL_FIELDS);
        return query.getResultList();
    }

    /**
     * find all fieldplans
     *
     * @return List of FieldPlans
     */
    public List<FieldPlan> findAllFieldPlans() {
        startTransaction();
        Query query = getEntityManager().createNamedQuery(FieldPlan.Q_FIND_ALL_FIELD_PLANS);
        return query.getResultList();
    }

    /**
     * Creates an Field within a transaction. Starts a transaction if it is not already started.
     * @param Builder of field
     */
    public PartField createPartField(PartField.Builder builder) {
        return createPartField(builder.build());
    }

    /**
     * Creates an Field within a transaction. Starts a transaction if it is not already started.
     * @param Builder of field
     */
    public PartField createPartField(PartField partField) {
        persistInTransaction(partField);
        return partField;
    }
}
